//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
#include "ManipulateMatrix.h"
#include "APCluster.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>

#include <iostream>
#include <fstream>

#include <zlib.h>
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
#define LENGTHLINE 100000
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
using namespace std;
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
ManipulateMatrix::ManipulateMatrix() {
    
  this->filenameMatrix = new String();
  this->filenameList   = new String();
  this->filenameOut    = new String();
  this->matrixValues   = NULL;
  this->matrixLabels   = new TListE <String> ();
  this->listOrdered    = new TListE <String> ();
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
ManipulateMatrix::ManipulateMatrix(const ManipulateMatrix& manipulateMatrix) {
    
  this->filenameMatrix = new String(manipulateMatrix.filenameMatrix);
  this->filenameList   = new String(manipulateMatrix.filenameList);
  this->filenameOut    = new String(manipulateMatrix.filenameOut);

  this->matrixLabels   = new TListE <String> (manipulateMatrix.matrixLabels);
  this->listOrdered    = new TListE <String> (manipulateMatrix.listOrdered);

  this->matrixValues   = new double*[manipulateMatrix.matrixLabels->Length()];

  for (int i = 0; i < manipulateMatrix.matrixLabels->Length(); i++) {
    this->matrixValues[i] = new double[manipulateMatrix.matrixLabels->Length()];
    for (int j = 0; j < manipulateMatrix.matrixLabels->Length(); j++)
      this->matrixValues[i][j] = manipulateMatrix.matrixValues[i][j];
  }
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
ManipulateMatrix::ManipulateMatrix(const ManipulateMatrix* manipulateMatrix) {

  this->filenameMatrix = new String(manipulateMatrix->filenameMatrix);
  this->filenameList   = new String(manipulateMatrix->filenameList);
  this->filenameOut    = new String(manipulateMatrix->filenameOut);

  this->matrixLabels   = new TListE <String> (manipulateMatrix->matrixLabels);
  this->listOrdered    = new TListE <String> (manipulateMatrix->listOrdered);
  
  this->matrixValues   = new double*[manipulateMatrix->matrixLabels->Length()];

  for (int i = 0; i < manipulateMatrix->matrixLabels->Length(); i++) {
    this->matrixValues[i] = new double[manipulateMatrix->matrixLabels->Length()];
    for (int j = 0; j < manipulateMatrix->matrixLabels->Length(); j++)
      this->matrixValues[i][j] = manipulateMatrix->matrixValues[i][j];
  }
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
ManipulateMatrix::~ManipulateMatrix() {

  if (this->filenameMatrix) delete this->filenameMatrix;
  if (this->filenameList)   delete this->filenameList;
  if (this->filenameOut)    delete this->filenameOut;
  
  for (int i = 0; i < this->matrixLabels->Length(); i++)
    if (this->matrixValues[i]) delete[] this->matrixValues[i];
  if (this->matrixValues) delete[] this->matrixValues;

  if (this->matrixLabels)   delete this->matrixLabels;
  if (this->listOrdered)    delete this->listOrdered;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void ManipulateMatrix::FilenameMatrix(String* filenameMatrix) {

  if (filenameMatrix) {
    if (this->filenameMatrix) delete this->filenameMatrix;
    this->filenameMatrix = filenameMatrix;
  }
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void ManipulateMatrix::FilenameList(String* filenameList) {

  if (filenameList) {
    if (this->filenameList) delete this->filenameList;
    this->filenameList = filenameList;
  }
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void ManipulateMatrix::FilenameOut(String* filenameOut) {

  if (filenameOut) {
    if (this->filenameOut) delete this->filenameOut;
    this->filenameOut = filenameOut;
  }
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void ManipulateMatrix::MatrixValues(double** matrixValues) {
    
  if (matrixValues && this->matrixLabels->Length()) {

    this->matrixValues = new double*[this->matrixLabels->Length()];

    for (int i = 0; i < this->matrixLabels->Length(); i++) {
      this->matrixValues[i] = new double[this->matrixLabels->Length()];
      for (int j = 0; j < this->matrixLabels->Length(); j++)
        this->matrixValues[i][j] = matrixValues[i][j];
    }
    
  }
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void ManipulateMatrix::MatrixLabels(TListE <String>* matrixLabels) {
  if (matrixLabels) *(this->matrixLabels) = *(matrixLabels);
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void ManipulateMatrix::ListOrdered(TListE <String>* listOrdered) {
  if (listOrdered) *(this->listOrdered) = *(listOrdered);
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
String* ManipulateMatrix::FilenameMatrix(void) {
  return this->filenameMatrix;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
String* ManipulateMatrix::FilenameList(void) {
  return this->filenameList;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
String* ManipulateMatrix::FilenameOut(void) {
  return this->filenameOut;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
double** ManipulateMatrix::MatrixValues(void) {
  return this->matrixValues;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
TListE <String>* ManipulateMatrix::MatrixLabels(void) {
  return this->matrixLabels;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
TListE <String>* ManipulateMatrix::ListOrdered(void) {
  return this->listOrdered;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
bool ManipulateMatrix::LoadData(void) {
    
  if (!this->filenameMatrix || !this->filenameMatrix->Length() ||
      !this->filenameList   || !this->filenameList->Length())
    return false;

  cout << "Loading Data from File...." << endl;

  fstream fileList;
  char    buffer[LENGTHLINE + 1], *buffer1;

  fileList.open(this->filenameList->Out(), fstream::in);

  if (!fileList.is_open()) return false;

  while (!fileList.eof()) {
    fileList.getline(buffer, LENGTHLINE);
    String* nameFile = new String(buffer);
    nameFile->Trim();
    if (nameFile->Length()) this->listOrdered->Add(nameFile);
  }
  fileList.close();

  gzFile           fileMatrix = gzopen(this->filenameMatrix->Out(), "rb9");

  bool             condLabels = false;

  TListE <String>* column     = new TListE <String> ();

  int              countRows  = 0;

  while (!gzeof(fileMatrix)) {

    gzgets(fileMatrix, buffer, LENGTHLINE);

    if (!condLabels) {

      this->matrixLabels->Clear();
      this->SplitColumn(buffer, this->matrixLabels, " ");

      this->matrixValues = new double*[this->matrixLabels->Length()];
      for (int i = 0; i < this->matrixLabels->Length(); i++) {
        this->matrixValues[i] = new double[this->matrixLabels->Length()];
        for (int j = 0; j < this->matrixLabels->Length(); j++)
          this->matrixValues[i][j] = 0;
      }
      
      condLabels = true;
      continue;

    }
    String line;
    
    line.In(buffer);

    if (countRows < this->matrixLabels->Length()) {
      this->SplitColumnValues(buffer, this->matrixValues[countRows++], " ");
    }
    
  }

  gzclose(fileMatrix);

  if (column) delete column;

  return true;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
bool ManipulateMatrix::OrderMatrixByList(void) {

  if (!this->matrixLabels->Length() || !this->listOrdered->Length()) {
    this->LoadData();
    if (!this->matrixLabels->Length() || !this->listOrdered->Length())
      return false;
  }

  cout << "Ordering the loaded Matrix [" << this->listOrdered->Length() << "]...." << endl;

  this->listOrdered->SetPosition(1);
  for (int i = 0; i < this->listOrdered->Length(); i++, this->listOrdered->SetNext()) {

    String*  pdbId     = this->listOrdered->GetCurrent()->SubStr(0, 4);
    String*  chain     = this->listOrdered->GetCurrent()->SubStr(5, 6);

    pdbId->Add("_");
    pdbId->Add(chain->Out());

    double*  columnPtr = NULL;
    double   cellPtr   = 0;

    //cout << this->listOrdered->GetCurrent()->Out() << " - " << this->matrixLabels->Length() << endl;
    
    this->matrixLabels->SetPosition(1);

    for (int j = 0; j < this->matrixLabels->Length(); j++) {

      if (this->matrixLabels->Get(j)->Contain(pdbId)) {

        //cout << pdbId->Out() << "I:" << i << " J: " << j << endl;

        TListE<String>::ExchangeNode(this->matrixLabels->GetNode(i), this->matrixLabels->GetNode(j));

        for (int z = 0; z < this->matrixLabels->Length(); z++) {
          cellPtr                  = this->matrixValues[z][i];
          this->matrixValues[z][i] = this->matrixValues[z][j];
          this->matrixValues[z][j] = cellPtr;
        }
        
        columnPtr                = this->matrixValues[i];
        this->matrixValues[i]    = this->matrixValues[j];
        this->matrixValues[j]    = columnPtr;

      }
        
    }

    if (pdbId) delete pdbId;
    if (chain) delete chain;

  }

  return true;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
bool ManipulateMatrix::WriteResultedMatrix(void) {

  if (!this->filenameOut || !this->filenameOut->Length() || !this->matrixValues)
    return false;

  fstream fileOut;

  fileOut.open(this->filenameOut->Out(), fstream::out);

  if (!fileOut.is_open()) return false;

  for (int i = 0; i < this->listOrdered->Length(); i++) {
    fileOut.width(10);
    fileOut.left;
    fileOut << this->listOrdered->Get(i)->Out() << " ";
  }
  fileOut << endl;

  for (int i = 0; i < this->listOrdered->Length(); i++) {
    for (int j = 0; j < this->listOrdered->Length(); j++) {
      fileOut.width(10); 
      fileOut.left;
      fileOut << this->matrixValues[i][j] << " ";
    }
    fileOut << endl;
  }

  fileOut.close();

  return true;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
bool ManipulateMatrix::WriteOriginalMatrix(void) {

  if (!this->filenameOut || !this->filenameOut->Length() || !this->matrixValues)
    return false;

  fstream fileOut;

  fileOut.open(this->filenameMatrix->Out(), fstream::out);

  if (!fileOut.is_open()) return false;

  TListE <int>* excludeList = new TListE <int> ();

  for (int i = 0; i < this->matrixLabels->Length(); i++) {

    bool condFind = false;

    for (int j = 0; j < this->listOrdered->Length(); j++) {
        
      String*  pdbId     = this->listOrdered->Get(j)->SubStr(0, 4);
      String*  chain     = this->listOrdered->Get(j)->SubStr(5, 6);

      pdbId->Add("_");
      pdbId->Add(chain->Out());

      if (this->matrixLabels->Get(i)->Contain(pdbId)) {

        condFind = true;
      
        if (pdbId) delete pdbId;
        if (chain) delete chain;

        break;
      }
      
      if (pdbId) delete pdbId;
      if (chain) delete chain;

    }

    if (condFind) {
      fileOut.width(10);
      fileOut.left;
      fileOut << this->matrixLabels->Get(i)->Out() << " ";
    }
    else {
      int* number = new int(i);
      excludeList->Add(number);
    }


  }
  fileOut << endl;

  for (int i = 0; i < this->matrixLabels->Length(); i++) {

    bool condWriteRow = true;

    excludeList->SetInitial();
    while (excludeList->SetNext()) 
      if (i == *(excludeList->GetCurrent())) {
        condWriteRow = false;
        break;
      }

    if (!condWriteRow) continue;

    for (int j = 0; j < this->matrixLabels->Length(); j++) {

      bool condWriteColumn = true;

      excludeList->SetInitial();
      while (excludeList->SetNext())
        if (j == *(excludeList->GetCurrent())) {
          condWriteColumn = false;
          break;
        }

      if (!condWriteColumn) continue;

      fileOut.width(10);
      fileOut.left;
      fileOut << this->matrixValues[i][j] << " ";
      
    }

    fileOut << endl;
    
  }

  fileOut.close();

  return true;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
bool ManipulateMatrix::SplitColumn(char* buffer, TListE<String>* columns, const char* characters) {

  if (!buffer || !columns || !characters) return false;

  char *part = strtok(buffer, characters);

  while (part) {
    String* cell = new String(part);
    cell->Trim();
    if (cell->Length()) columns->Add(cell);
    part = strtok(NULL, characters);
  }

  return true;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
bool ManipulateMatrix::SplitColumnValues(char* buffer, double* columns, const char* characters) {

  if (!buffer || !columns || !characters) return false;

  char *part    = strtok(buffer, characters);
  int   counter = 0;

  while (part) {
    String* cell = new String(part);
    cell->Trim();
    if (cell->Length()) columns[counter++] = atof(cell->Out());
    part = strtok(NULL, characters);
  }

  return true;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
